---
title: Subscriptions in Apollo Server
description: Persistent GraphQL read operations
---

<Note>

**Apollo Server does not provide built-in support for subscriptions in non-federated graphs.**<br/><br/>

If your Apollo Server is a subgraph running behind GraphOS Router, you can use the [subscription callback plugin](../api/plugin/subscription-callback) to allow your server to respond to subscription operations.<br/><br/>

For non-federated graphs, you can enable support for subscriptions as [described below](#enabling-subscriptions) by adding the third-party `graphql-ws` library to your server.<br/><br/>

(We previously documented how to use the library `subscriptions-transport-ws`, which is no longer actively maintained. For help migrating from `subscriptions-transport-ws`, see [the Apollo Server 3 docs](/apollo-server/v3/data/subscriptions/#switching-to-graphql-ws).)

</Note>

**Subscriptions** are long-lasting GraphQL read operations that can update their result whenever a particular server-side event occurs. Most commonly, updated results are _pushed_ from the server to subscribing clients. For example, a chat application's server might use a subscription to push newly received messages to all clients in a particular chat room.

Because subscription updates are usually pushed by the server (instead of polled by the client), they generally use [the WebSocket protocol](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) instead of HTTP.

> **Important:** Compared to queries and mutations, subscriptions are significantly more complex to implement. Before you begin, [confirm that your use case requires subscriptions](/react/data/subscriptions/#when-to-use-subscriptions).

## Schema definition

Your schema's `Subscription` type defines top-level fields that clients can subscribe to:

```graphql
type Subscription {
  postCreated: Post
}
```

The `postCreated` field will update its value whenever a new `Post` is created on the backend, thus pushing the `Post` to subscribing clients.

Clients can subscribe to the `postCreated` field with a GraphQL string, like this:

```graphql
subscription PostFeed {
  postCreated {
    author
    comment
  }
}
```

> Each subscription operation can subscribe to only _one_ field of the `Subscription` type.

## Enabling subscriptions

> Subscriptions are **not** supported by Apollo Server's `startStandaloneServer` function. To enable subscriptions, you must first [swap to using the `expressMiddleware` function](../api/express-middleware) (or any other Apollo Server integration package that supports subscriptions).
>
> The following steps assume you've already swapped to [`expressMiddleware`](../api/express-middleware).

To run both an Express app _and_ a separate WebSocket server for subscriptions, we'll create an `http.Server` instance that effectively wraps the two and becomes our new `listen`er.

1. Install `graphql-ws`, `ws`, and `@graphql-tools/schema`:

   ```bash
   npm install graphql-ws ws @graphql-tools/schema
   ```

2. Add the following imports to the file where you initialize your `ApolloServer` instance (we'll use these in later steps):

   ```ts title="index.ts"
   import { createServer } from 'http';
   import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
   import { makeExecutableSchema } from '@graphql-tools/schema';
   import { WebSocketServer } from 'ws';
   import { useServer } from 'graphql-ws/use/ws';
   ```

3. Next, in order to set up both the HTTP and subscription servers, we need to first create an `http.Server`. Do this by passing your Express `app` to the `createServer` function, which we imported from the `http` module:

   ```ts title="index.ts"
   // This `app` is the returned value from `express()`.
   const httpServer = createServer(app);
   ```

4. Create an instance of `GraphQLSchema` (if you haven't already).

   > If you already pass the `schema` option to the `ApolloServer` constructor (instead of `typeDefs` and `resolvers`), you can skip this step.

   The subscription server (which we'll instantiate next) doesn't take `typeDefs` and `resolvers` options. Instead, it takes an executable `GraphQLSchema`. We can pass this `schema` object to both the subscription server and `ApolloServer`. This way, we make sure that the same schema is being used in both places.

   ```ts title="index.ts"
   const schema = makeExecutableSchema({ typeDefs, resolvers });
   // ...
   const server = new ApolloServer({
     schema,
   });
   ```

5. Create a `WebSocketServer` to use as your subscription server.

   ```ts title="index.ts"
   // Creating the WebSocket server
   const wsServer = new WebSocketServer({
     // This is the `httpServer` we created in a previous step.
     server: httpServer,
     // Pass a different path here if app.use
     // serves expressMiddleware at a different path
     path: '/subscriptions',
   });

   // Hand in the schema we just created and have the
   // WebSocketServer start listening.
   const serverCleanup = useServer({ schema }, wsServer);
   ```

6. Add [plugins](../integrations/plugins) to your `ApolloServer` constructor to shutdown both the HTTP server and the `WebSocketServer`:

   ```ts title="index.ts"
   const server = new ApolloServer({
     schema,
     plugins: [
       // Proper shutdown for the HTTP server.
       ApolloServerPluginDrainHttpServer({ httpServer }),

       // Proper shutdown for the WebSocket server.
       {
         async serverWillStart() {
           return {
             async drainServer() {
               await serverCleanup.dispose();
             },
           };
         },
       },
     ],
   });
   ```

7. Finally, ensure you are `listen`ing to your `httpServer`.

   Most Express applications call `app.listen(...)`, but for your setup change this to `httpServer.listen(...)` using the same arguments. This way, the server starts listening on the HTTP and WebSocket transports simultaneously.

A completed example of setting up subscriptions is shown below:

<ExpansionPanel title="Click to expand">

<MultiCodeBlock>

```ts title="index.ts"
import { ApolloServer } from '@apollo/server';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
import { expressMiddleware } from '@as-integrations/express5';
import { createServer } from 'http';
import express from 'express';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { WebSocketServer } from 'ws';
import { useServer } from 'graphql-ws/use/ws';
import cors from 'cors';
import resolvers from './resolvers';
import typeDefs from './typeDefs';

// Create the schema, which will be used separately by ApolloServer and
// the WebSocket server.
const schema = makeExecutableSchema({ typeDefs, resolvers });

// Create an Express app and HTTP server; we will attach both the WebSocket
// server and the ApolloServer to this HTTP server.
const app = express();
const httpServer = createServer(app);

// Create our WebSocket server using the HTTP server we just set up.
const wsServer = new WebSocketServer({
  server: httpServer,
  path: '/subscriptions',
});
// Save the returned server's info so we can shutdown this server later
const serverCleanup = useServer({ schema }, wsServer);

// Set up ApolloServer.
const server = new ApolloServer({
  schema,
  plugins: [
    // Proper shutdown for the HTTP server.
    ApolloServerPluginDrainHttpServer({ httpServer }),

    // Proper shutdown for the WebSocket server.
    {
      async serverWillStart() {
        return {
          async drainServer() {
            await serverCleanup.dispose();
          },
        };
      },
    },
  ],
});

await server.start();
app.use(
  '/graphql',
  cors<cors.CorsRequest>(),
  express.json(),
  expressMiddleware(server),
);

const PORT = 4000;
// Now that our HTTP server is fully set up, we can listen to it.
httpServer.listen(PORT, () => {
  console.log(`Server is now running on http://localhost:${PORT}/graphql`);
});
```

</MultiCodeBlock>

</ExpansionPanel>

> ⚠️ **Running into an error?** If you're using the `graphql-ws` library, your specified subscription protocol must be consistent across your backend, frontend, and **every other tool** you use (including [Apollo Sandbox](/graphos/explorer/subscriptions-explorer)). For more information, see [Switching from `subscriptions-transport-ws`](/apollo-server/v3/data/subscriptions/#switching-to-graphql-ws) in the Apollo Server 3 docs.

## Resolving a subscription

Resolvers for `Subscription` fields differ from resolvers for fields of other types. Specifically, `Subscription` field resolvers are **objects** that define a `subscribe` function:

```ts title="index.ts"
const resolvers = {
  Subscription: {
    hello: {
      // Example using an async generator
      subscribe: async function* () {
        for await (const word of ['Hello', 'Bonjour', 'Ciao']) {
          yield { hello: word };
        }
      },
    },
    postCreated: {
      // More on pubsub below
      subscribe: () => pubsub.asyncIterator(['POST_CREATED']),
    },
  },
  // ...other resolvers...
};
```

The `subscribe` function **must** return an object of type `AsyncIterator`, a standard interface for iterating over asynchronous results. In the above `postCreated.subscribe` field, an `AsyncIterator` is generated by `pubsub.asyncIterator` (more on this below).

### The `PubSub` class

> The `PubSub` class is **not** recommended for production environments, because it's an in-memory event system that only supports a single server instance. After you get subscriptions working in development, we strongly recommend switching it out for a different subclass of the abstract [`PubSubEngine` class](https://github.com/apollographql/graphql-subscriptions/blob/master/src/pubsub-engine.ts). Recommended subclasses are listed in [Production `PubSub` libraries](#production-pubsub-libraries).

You can use the **publish-subscribe** (**pub/sub**) model to track events that update active subscriptions. The [`graphql-subscriptions` library](https://github.com/apollographql/graphql-subscriptions) provides the `PubSub` class as a basic in-memory event bus to help you get started:

To use the `graphql-subscriptions` package, first install it like so:

```shell
npm install graphql-subscriptions
```

A `PubSub` instance enables your server code to both `publish` events to a particular label and listen for events associated with a particular label. We can create a `PubSub` instance like so:

```ts
import { PubSub } from 'graphql-subscriptions';

const pubsub = new PubSub();
```

### Publishing an event

You can publish an event using the `publish` method of a `PubSub` instance:

```ts
pubsub.publish('POST_CREATED', {
  postCreated: {
    author: 'Ali Baba',
    comment: 'Open sesame',
  },
});
```

- The first parameter is the name of the event label you're publishing to, as a string.
  - _You don't need to register a label name before publishing to it._
- The second parameter is the payload associated with the event.
  - _The payload should include whatever data is necessary for your resolvers to populate the associated `Subscription` field and its subfields._

When working with GraphQL subscriptions, you `publish` an event whenever a subscription's return value should be updated. One common cause of such an update is a mutation, but _any_ back-end logic might result in changes that should be `publish`ed.

As an example, let's say our GraphQL API supports a `createPost` mutation:

```graphql
type Mutation {
  createPost(author: String, comment: String): Post
}
```

A basic resolver for `createPost` might look like this:

```ts {3-6}
const resolvers = {
  Mutation: {
    createPost(parent, args, { postController }) {
      // Datastore logic lives in postController
      return postController.createPost(args);
    },
  },
  // ...other resolvers...
};
```

Before we persist the new post's details in our datastore, we can `publish` an event that _also_ includes those details:

```ts
const resolvers = {
  Mutation: {
    createPost(parent, args, { postController }) {
      pubsub.publish('POST_CREATED', { postCreated: args }); // highlight-line
      return postController.createPost(args);
    },
  },
  // ...other resolvers...
};
```

Next, we can listen for this event in our `Subscription` field's resolver.

### Listening for events

An [`AsyncIterator`](https://github.com/apollographql/graphql-subscriptions/blob/master/src/pubsub-async-iterator.ts) object listens for events that are associated with a particular label (or set of labels) and adds them to a queue for processing.

You can create an `AsyncIterator` by calling the `asyncIterator` method of `PubSub` and passing in an array containing the names of the event labels that this `AsyncIterator` should listen for.

```ts
pubsub.asyncIterator(['POST_CREATED']);
```

Every `Subscription` field resolver's `subscribe` function must return an `AsyncIterator` object.

This brings us back to the code sample at the top of [Resolving a subscription](#resolving-a-subscription):

```ts title="index.ts"
const resolvers = {
  Subscription: {
    postCreated: {
      subscribe: () => pubsub.asyncIterator(['POST_CREATED']),
    },
  },
  // ...other resolvers...
};
```

With this `subscribe` function set, Apollo Server uses the payloads of `POST_CREATED` events to push updated values for the `postCreated` field.

### Filtering events

Sometimes, a client should only receive updated subscription data if that data meets certain criteria. To support this, you can call the `withFilter` helper function in your `Subscription` field's resolver.

#### Example

Let's say our server provides a `commentAdded` subscription, which should notify clients whenever a comment is added to a specified code repository. A client can execute a subscription that looks like this:

```graphql
subscription ($repoName: String!) {
  commentAdded(repoFullName: $repoName) {
    id
    content
  }
}
```

This presents a potential issue: our server probably [publishes](#publishing-an-event) a `COMMENT_ADDED` event whenever a comment is added to _any_ repository. This means that the `commentAdded` resolver executes for _every_ new comment, regardless of which repository it's added to. As a result, subscribing clients might receive data they don't want (or shouldn't even have access to).

To fix this, we can use the `withFilter` helper function to control updates on a per-client basis.

Here's an example resolver for `commentAdded` that uses the `withFilter` function:

```ts {5-12}
import { withFilter } from 'graphql-subscriptions';

const resolvers = {
  Subscription: {
    commentAdded: {
      subscribe: withFilter(
        () => pubsub.asyncIterator('COMMENT_ADDED'),
        (payload, variables) => {
          // Only push an update if the comment is on
          // the correct repository for this operation
          return (
            payload.commentAdded.repository_name === variables.repoFullName
          );
        },
      ),
    },
  },
  // ...other resolvers...
};
```

The `withFilter` function takes two parameters:

- The first parameter is exactly the function you would use for `subscribe` if you _weren't_ applying a filter.
- The second parameter is a **filter function** that returns `true` if a subscription update _should_ be sent to a particular client, and `false` otherwise (`Promise<boolean>` is also allowed). This function takes two parameters of its own:
  - `payload` is the payload of the event that was published.
  - `variables` is an object containing all arguments the client provided when initiating their subscription.

Use `withFilter` to make sure clients get exactly the subscription updates they want (and are allowed to receive).

## Basic runnable example

An example server is available on [GitHub](https://github.com/apollographql/docs-examples/blob/main/apollo-server/v5/subscriptions-graphql-ws/src/index.ts) and CodeSandbox:

<ButtonLink
  href="https://codesandbox.io/s/github/apollographql/docs-examples/tree/main/apollo-server/v5/subscriptions-graphql-ws?fontsize=14&hidenavigation=1&initialpath=%2Fgraphql&theme=dark"
  size="lg"
>
  Edit in CodeSandbox
</ButtonLink>

The server exposes one subscription (`numberIncremented`) that returns an integer that's incremented on the server every second. Here's an example subscription that you can run against your server:

```graphql
subscription IncrementingNumber {
  numberIncremented
}
```

> If you don't see the result of your subscription operation you might need to [adjust your Sandbox settings to use the `graphql-ws` protocol](/graphos/explorer/subscriptions-explorer).

After you start up the server in CodeSandbox, follow the instructions in the browser to test running the `numberIncremented` subscription in Apollo Sandbox. You should see the subscription's value update every second.

## Operation context

When [initializing context](../data/context) for a query or mutation, you usually extract HTTP headers and other request metadata from the `req` object provided to the `context` function.

**For subscriptions,** you can extract information from a client's request by adding [options](https://the-guild.dev/graphql/ws/docs/interfaces/server.ServerOptions) to the first argument passed to the `useServer` function.

For example, you can provide a [`context` property](https://the-guild.dev/graphql/ws/docs/interfaces/server.ServerOptions#context) to add values to your GraphQL operation [`contextValue`](./context#the-contextvalue-object):

```ts
// ...
useServer(
  {
    // Our GraphQL schema.
    schema,
    // Adding a context property lets you add data to your GraphQL operation contextValue
    context: async (ctx, msg, args) => {
      // You can define your own function for setting a dynamic context
      // or provide a static value
      return getDynamicContext(ctx, msg, args);
    },
  },
  wsServer,
);
```

Notice that the first parameter passed to the `context` function above is `ctx`. The [`ctx object`](https://the-guild.dev/graphql/ws/docs/interfaces/server.Context) represents the context of your _subscription server_, not the _GraphQL operation `contextValue`_ that's passed to your resolvers.

You can access the parameters of a client's `subscription` request to your WebSocket server via the `ctx.connectionParams` property.

Below is an example of the common use case of extracting an authentication token from a client `subscription` request and using it to find the current user:

```ts
const getDynamicContext = async (ctx, msg, args) => {
  // ctx is the graphql-ws Context where connectionParams live
  if (ctx.connectionParams.authentication) {
    const currentUser = await findUser(ctx.connectionParams.authentication);
    return { currentUser };
  }
  // Otherwise let our resolvers know we don't have a current user
  return { currentUser: null };
};

useServer(
  {
    schema,
    context: async (ctx, msg, args) => {
      // Returning an object will add that information to
      // contextValue, which all of our resolvers have access to.
      return getDynamicContext(ctx, msg, args);
    },
  },
  wsServer,
);
```

Putting it all together, the `useServer.context` function returns an _object_, `contextValue`, which is available to your resolvers.

Note that the `context` option is called once per `subscription` request, not once per event emission. This means that in the above example, every time a client sends a `subscription` operation, we check their authentication token.

### `onConnect` and `onDisconnect`

You can configure the subscription server's behavior whenever a client connects (`onConnect`) or disconnects (`onDisconnect`).

Defining an `onConnect` function enables you to reject a particular incoming connection by returning `false` or by throwing an exception. This can be helpful if you want to check authentication when a client first connects to your subscription server.

You provide these functions as options to the first argument of `useServer`, like so:

```ts {5,12}
useServer(
  {
    schema,
    // As before, ctx is the graphql-ws Context where connectionParams live.
    onConnect: async (ctx) => {
      // Check authentication every time a client connects.
      if (tokenIsNotValid(ctx.connectionParams)) {
        // You can return false to close the connection  or throw an explicit error
        throw new Error('Auth token missing!');
      }
    },
    onDisconnect(ctx, code, reason) {
      console.log('Disconnected!');
    },
  },
  wsServer,
);
```

For more examples of using `onConnect` and `onDisconnect`, see the [`graphql-ws` recipes documentation](https://the-guild.dev/graphql/ws/recipes).

### Example: Authentication with Apollo Client

> If you plan to use subscriptions with Apollo Client, ensure **[both your client and server subscription protocols are consistent](/apollo-server/v3/data/subscriptions/#switching-to-graphql-ws)** for the subscription library you're using (this example uses the `graphql-ws` library).

In Apollo Client, the `GraphQLWsLink` constructor supports adding information to `connectionParams` ([example](/react/data/subscriptions/#5-authenticate-over-websocket-optional)). Those `connectionParams` are sent to your server when it connects, allowing you to extract information from the client request by accessing `Context.connectionParams`.

Let's suppose we create our subscription client like so:

```ts
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';

const wsLink = new GraphQLWsLink(
  createClient({
    url: 'ws://localhost:4000/subscriptions',
    connectionParams: {
      authentication: user.authToken,
    },
  }),
);
```

The `connectionParams` argument (which contains the information provided by the client) is passed to your server, enabling you to validate the user's credentials.

From there you can use the [`useServer.context` property](#operation-context) to authenticate the user, returning an object that's passed into your resolvers as the `context` argument during execution.

For our example, we can use the `connectionParams.authentication` value provided by the client to look up the related user before passing that user along to our resolvers:

```ts
const findUser = async (authToken) => {
  // Find a user by their auth token
};

const getDynamicContext = async (ctx, msg, args) => {
  if (ctx.connectionParams.authentication) {
    const currentUser = await findUser(ctx.connectionParams.authentication);
    return { currentUser };
  }
  // Let the resolvers know we don't have a current user so they can
  // throw the appropriate error
  return { currentUser: null };
};

// ...
useServer(
  {
    // Our GraphQL schema.
    schema,
    context: async (ctx, msg, args) => {
      // This will be run every time the client sends a subscription request
      return getDynamicContext(ctx, msg, args);
    },
  },
  wsServer,
);
```

To sum up, the example above looks up a user based on the authentication token sent with each subscription request before returning the user object to be used by our resolvers. If no user exists or the lookup otherwise fails, our resolvers can throw an error and the corresponding `subscription` operation is not executed.

## Production `PubSub` libraries

[As mentioned above](#the-pubsub-class), the `PubSub` class is _not_ recommended for production environments, because its event-publishing system is in-memory. This means that events published by _one_ instance of your GraphQL server are not received by subscriptions that are handled by _other_ instances.

Instead, you should use a subclass of the [`PubSubEngine` abstract class](https://github.com/apollographql/graphql-subscriptions/blob/master/src/pubsub-engine.ts) that you can back with an external datastore such as Redis or Kafka.

The following are community-created `PubSub` libraries for popular event-publishing systems:

- [Solace](https://github.com/solacecommunity/graphql-solace-subscriptions)
- [Redis](https://github.com/davidyaha/graphql-redis-subscriptions)
- [Google PubSub](https://github.com/axelspringer/graphql-google-pubsub)
- [MQTT enabled broker](https://github.com/davidyaha/graphql-mqtt-subscriptions)
- [RabbitMQ](https://github.com/cdmbase/graphql-rabbitmq-subscriptions)
- [AMQP (RabbitMQ)](https://github.com/Surnet/graphql-amqp-subscriptions)
- [Kafka](https://github.com/ancashoria/graphql-kafka-subscriptions)
- [Postgres](https://github.com/GraphQLCollege/graphql-postgres-subscriptions)
- [Postgres With Typescript](https://github.com/siamahnaf/graphql-pg-subscriptions)
- [Google Cloud Firestore](https://github.com/MrBoolean/graphql-firestore-subscriptions)
- [Ably Realtime](https://github.com/ably-labs/graphql-ably-pubsub)
- [Google Firebase Realtime Database](https://github.com/swantzter/graphql-firebase-subscriptions)
- [Azure SignalR Service](https://github.com/aaronpowell/graphql-azure-subscriptions)
- [Azure ServiceBus](https://github.com/abdomohamed/graphql-azure-servicebus-subscriptions)
- [MongoDB](https://github.com/mjwheatley/graphql-mongodb-subscriptions)

If none of these libraries fits your use case, you can also create your own `PubSubEngine` subclass. If you create a new open-source library, click **Edit on GitHub** to let us know about it!
